#include "videosurface.h"

#include <QDebug>
#include <QImage>
#include <QQuickWindow>
#include <chrono>

#include "videosurface/shaderloader.h"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
}

using namespace std::chrono;

static const GLfloat vertices[] = {
    // 顶点坐标
    -1.0, -1.0, 1.0,  // 左下
    -1.0, 1.0, 1.0,   // 左上
    1.0, 1.0, 1.0,    // 右上
    1.0, -1.0, 1.0,   // 右下

    // 纹理坐标
    0.0f, 0.0f,  // 左下角
    0.0f, 1.0f,  // 左上
    1.0f, 1.0f,  // 右上
    1.0f, 0.0f,  // 右下角
};

/***************************** VideoSurfaceRenderer impl
 * ********************************/
VideoSurfaceRenderer::VideoSurfaceRenderer()
    : m_texRGB(QOpenGLTexture::Target2D) {}

VideoSurfaceRenderer::~VideoSurfaceRenderer() {
  m_vbo.destroy();
  m_vao.destroy();
}

void VideoSurfaceRenderer::render() {
  // 清屏
  glClearColor(.2, .3, .3, 1.);
  glClear(GL_COLOR_BUFFER_BIT);

  // 绑定 vao
  if (m_lastFrame) {
    glActiveTexture(GL_TEXTURE0);
    m_vao.bind();
    m_texRGB.bind();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_lastFrame->width,
                 m_lastFrame->height, 0, GL_RGB, GL_UNSIGNED_BYTE,
                 m_lastFrame->data[0]);

    // 使用 program
    m_program.bind();
    m_program.setUniformValue("rbg_tex", 0);
    // 画
    //    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    // 获取渲染后的图像
    if (nullptr != m_pImg) {
      delete m_pImg;
    }
    m_pImg = new QImage(m_lastFrame->width, m_lastFrame->height,
                        QImage::Format_RGB888);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glReadPixels(0, 0, m_lastFrame->width, m_lastFrame->height, GL_RGB,
                 GL_UNSIGNED_BYTE, m_pImg->bits());

    // 解绑
    m_vao.release();
    m_texRGB.release();
    m_program.release();
  }
}

/**
 * @brief 同步
 *
 * @param item
 */
void VideoSurfaceRenderer::synchronize(QQuickFramebufferObject *item) {
  VideoSurface *p = static_cast<VideoSurface *>(item);

  //  if (nullptr != m_lastFrame) {
  //    av_frame_free(&m_lastFrame);
  //  }

  m_lastFrame = p->getLastFrame();
  if (m_pImg) {
    std::shared_ptr<QImage> spImage(m_pImg);
    p->sendImage(spImage);
    m_pImg = nullptr;
  }
}

/**
 * @brief 初始化
 *
 * @return true
 * @return false
 */
bool VideoSurfaceRenderer::init() {
  initializeOpenGLFunctions();
  m_vao.create();
  m_vbo.create();
  m_texRGB.create();

  if (!initShader()) {
    return false;
  }

  if (!initVertex()) {
    return false;
  }

  return true;
}

bool VideoSurfaceRenderer::initShader() {
  if (!m_program.addShaderFromSourceFile(QOpenGLShader::Vertex,
                                         ShaderLoader::getVert()) ||
      !m_program.addShaderFromSourceFile(QOpenGLShader::Fragment,
                                         ShaderLoader::getFrag())) {
    return false;
  }
  if (!m_program.link()) {
    return false;
  }

  return true;
}

bool VideoSurfaceRenderer::initVertex() {
  m_vao.bind();

  m_vbo.bind();
  m_vbo.allocate(vertices, sizeof(vertices));

  // 顶点坐标
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(GLfloat),
                        (void *)0);
  glEnableVertexAttribArray(0);

  // 纹理坐标
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat),
                        (void *)(12 * sizeof(GLfloat)));
  glEnableVertexAttribArray(1);

  m_vao.release();
  m_vbo.release();

  return true;
}

/*************** VideoSurface impl ********************/
VideoSurface::VideoSurface(QQuickItem *parent)
    : QQuickFramebufferObject{parent}, m_pScreenRecoder{new ScreenRecoder} {
  m_pScreenRecoder->moveToThread(m_pScreenRecoder);
  m_iFpsCountTimerId = startTimer(1s);
}

VideoSurface::~VideoSurface() {}

QQuickFramebufferObject::Renderer *VideoSurface::createRenderer() const {
  m_pRender = new VideoSurfaceRenderer();
  if (!m_pRender->init()) return nullptr;

  return m_pRender;
}

void VideoSurface::sendImage(std::shared_ptr<QImage> img) {
  emit newImage(img);
}

void VideoSurface::timerEvent(QTimerEvent *event) {
  if (m_iFpsCountTimerId == event->timerId()) {
    emit fpsChanged(m_iFpsCount);
    m_iFpsCount = 0;
  }
}

void VideoSurface::onAVFrame(std::shared_ptr<AVFrame> frame) {
  m_lastFrame = frame;

  ++m_iFpsCount;
  update();
}

void VideoSurface::setSource(VideoCapturer *src) {
  if (nullptr == src) {
    return;
  }

  m_pVideoCapturer = src;
  connect(src, &VideoCapturer::appending_frame, this, &VideoSurface::onAVFrame);
}

void VideoSurface::startRecord() {
  if (!m_pScreenRecoder->isRunning()) {
    connect(this, &VideoSurface::newImage, m_pScreenRecoder,
            &ScreenRecoder::saveImage);
    m_pScreenRecoder->start();
  }
}

void VideoSurface::stopRecord() {
  if (m_pScreenRecoder->isRunning()) {
    m_pScreenRecoder->quit();
    m_pScreenRecoder->wait();
    disconnect(this, &VideoSurface::newImage, m_pScreenRecoder,
               &ScreenRecoder::saveImage);
  }
}
